﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Gargoyle.Roton
{
    public class ByteString
    {
        private static Encoding encoding = Encoding.GetEncoding(437);

        private byte[] Bytes;

        /// <summary>
        /// Create a ByteString with empty contents.
        /// </summary>
        public ByteString()
        {
            Bytes = new byte[0];
        }

        /// <summary>
        /// Create a ByteString from a String.
        /// </summary>
        /// <param name="s"></param>
        public ByteString(string s)
        {
            Bytes = encoding.GetBytes(s);
        }

        /// <summary>
        /// Create a ByteString from a Byte array.
        /// </summary>
        /// <param name="b"></param>
        public ByteString(byte[] b)
        {
            try
            {
                Bytes = new byte[b.Length];
                Array.Copy(b, Bytes, b.Length);
            }
            catch
            {
                Bytes = new byte[] { };
            }
        }

        /// <summary>
        /// Create a deep copy of another ByteString.
        /// </summary>
        /// <param name="bs"></param>
        public ByteString(ByteString bs)
        {
            try
            {
                Bytes = new byte[bs.Length];
                Array.Copy(bs.Bytes, Bytes, Bytes.Length);
            }
            catch
            {
                Bytes = new byte[] { };
            }
        }

        /// <summary>
        /// Gets or sets a byte within the ByteString.
        /// </summary>
        /// <param name="index">Index of the ByteString data to change.</param>
        /// <returns>Data of the ByteString at the specified index.</returns>
        public byte this[int index]
        {
            get
            {
                return Bytes[index];
            }
            set 
            {
                Bytes[index] = value;
            }
        }

        public static implicit operator ByteString(byte b)
        {
            return new ByteString(new byte[]{b});
        }

        public static implicit operator ByteString(byte[] b)
        {
            return new ByteString(b);
        }

        public static implicit operator ByteString(string s)
        {
            return new ByteString(s);
        }

        public static implicit operator byte[](ByteString bs)
        {
            try
            {
                byte[] result = new byte[bs.Length];
                Array.Copy(bs.Bytes, result, bs.Length);
                return result;
            }
            catch
            {
                return new byte[] { };
            }
        }

        public static implicit operator string(ByteString bs)
        {
            return (encoding.GetString(bs.Bytes));
        }

        public static bool operator ==(ByteString bs1, ByteString bs2)
        {
            if ((object)bs1 == (object)bs2)
                return true;
            if ((object)bs1 == null)
                return false;
            if ((object)bs2 == null)
                return false;

            if (bs1.Length != bs2.Length)
                return false;

            int length = bs1.Length;
            for (int i = 0; i < length; i++)
            {
                if (bs1[i] != bs2[i])
                    return false;
            }

            return true;
        }

        public static bool operator !=(ByteString bs1, ByteString bs2)
        {
            return !(bs1 == bs2);
        }

        public static ByteString operator +(ByteString bs1, ByteString bs2)
        {
            List<byte> builder = new List<byte>();
            builder.AddRange(bs1.Bytes);
            builder.AddRange(bs2.Bytes);
            return (ByteString)builder.ToArray();
        }

        /// <summary>
        /// Create a deep copy of this ByteString.
        /// </summary>
        /// <returns></returns>
        public ByteString Clone()
        {
            return new ByteString(Bytes);
        }

        /// <summary>
        /// Append a byte to the end of this ByteString.
        /// </summary>
        /// <param name="character">The byte to append.</param>
        public void Append(byte character)
        {
            Resize(Length + 1);
            Bytes[Bytes.Length - 1] = character;
        }

        /// <summary>
        /// Retrieve the number of occurrences of a byte within this ByteString.
        /// </summary>
        /// <param name="character">The byte to search for.</param>
        /// <returns>The number of occurrences of the specified byte.</returns>
        public int CountOf(byte character)
        {
            int result = 0;
            for (int i = 0; i < Bytes.Length; i++)
            {
                if (Bytes[i] == character)
                    result++;
            }
            return result;
        }

        private string DebugAsString
        {
            get
            {
                return ToString();
            }
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Retreive the first occurrence of a byte. Returns -1 if the byte is not present.
        /// </summary>
        /// <param name="character">The byte to search for.</param>
        /// <returns>The first occurrence of the specific byte.</returns>
        public int IndexOf(byte character)
        {
            return IndexOf(character, 0);
        }

        /// <summary>
        /// Retreive the first occurrence of a byte starting at a specific index. Returns -1 if the byte is not present.
        /// </summary>
        /// <param name="character">The byte to search for.</param>
        /// <param name="start">The starting index.</param>
        /// <returns>The first occurrence of the specific byte.</returns>
        public int IndexOf(byte character, int start)
        {
            for (int i = start; i < Bytes.Length; i++)
            {
                if (Bytes[i] == character)
                    return i;
            }
            return -1;
        }

        /// <summary>
        /// Retrieve the first occurrence of a ByteString within this ByteString.
        /// </summary>
        /// <param name="bs">The ByteString to search for.</param>
        /// <returns>The index of the specific ByteString.</returns>
        public int IndexOf(ByteString bs)
        {
            return IndexOf(bs, 0);
        }

        /// <summary>
        /// Retrieve the first occurrence of a ByteString starting at a specific index.
        /// </summary>
        /// <param name="bs">The ByteString to search for.</param>
        /// <param name="start">The starting index.</param>
        /// <returns>The index of the specific ByteString.</returns>
        public int IndexOf(ByteString bs, int start)
        {
            string bsString = (string)bs;
            return bsString.IndexOf(bs, start);
        }

        /// <summary>
        /// Get the length of the ByteString in bytes.
        /// </summary>
        public int Length
        {
            get
            {
                return Bytes.Length;
            }
        }

        /// <summary>
        /// Resize the ByteString. If the new size is larger, the extra bytes are initialized to 0.
        /// </summary>
        /// <param name="newSize"></param>
        public void Resize(int newSize)
        {
            Array.Resize<byte>(ref Bytes, newSize);
        }

        /// <summary>
        /// Split this ByteString into a collection of ByteStrings using a separator byte.
        /// </summary>
        /// <param name="separator">The separator byte.</param>
        /// <returns>A collection of ByteStrings that has been split.</returns>
        public ByteString[] Split(byte separator)
        {
            List<ByteString> result = new List<ByteString>();
            List<byte> section = new List<byte>();

            for (int i = 0; i < Bytes.Length; i++)
            {
                if (Bytes[i] == separator)
                {
                    result.Add(section.ToArray());
                    section.Clear();
                }
                else
                {
                    section.Add(Bytes[i]);
                }
            }
            result.Add(section.ToArray());

            return result.ToArray();
        }

        /// <summary>
        /// Return the String representation of this ByteString.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return (encoding.GetString(Bytes));
        }
    }

}
